package lexer

import (
    "caculator-script/lexer/state"
    "caculator-script/lexer/token"
)

func IsNumber(c uint8) bool {
    if c >= '0' && c < '9' {
        return true
    }
    return false
}

func IsAlphabet(c uint8) bool {
    if c >= 'a' && c < 'z' {
        return true
    } else if c >= 'A' && c < 'Z' {
        return true
    }
    return false
}

type Lexer interface {
    Tokenize(string) token.TokenReader
}

// lexer 词法解析器
type lexer struct {
    state state.Type // 存储当前解析状态
    
    tokens     []token.Token // 存储 Token 数组
    tokenValue string        // 存储临时 token
    lineNumber int
    
    rawScript string // 原始的脚本值
    
    readerBuilder token.BuildReader
    tokenBuilder  token.BuildToken
}

func NewDefaultLexer() Lexer {
    return NewLexer(token.NewSimpleReader, token.NewSimpleToken)
}

func NewLexer(readerBuilder token.BuildReader, tokenBuilder token.BuildToken) Lexer {
    return &lexer{
        readerBuilder: readerBuilder,
        tokenBuilder:  tokenBuilder,
    }
}

func (l *lexer) Tokenize(code string) token.TokenReader {
    l.rawScript = code
    l.decode()
    return l.readerBuilder(l.tokens)
}

// 解析token参数
func (l *lexer) decode() {
    l.state = state.Initial
    l.tokens = make([]token.Token, 0)
    l.lineNumber = 1
    
    for i := 0; i < len(l.rawScript); i++ {
        
        l.stateSwitch(l.rawScript[i])
        if l.rawScript[i] == '\n' {
            l.lineNumber += 1
        }
    }
    l.stateSwitch(0)
}

// stateSwitch
func (l *lexer) stateSwitch(c uint8) {
    switch l.state {
    case state.Initial:
        l.initial(c)
    case state.IdInt1, state.IdInt2, state.IdInt3, state.Id:
        l.idInt(c)
    case state.IntLiteral:
        if IsNumber(c) {
            l.tokenValueAdd(c)
        } else {
            l.tokensAdd(token.IntLiteral)
            l.initial(c)
        }
    case state.Plus, state.Minus, state.Star, state.Slash, state.RightParen, state.LeftParen, state.SemiColon, state.Assignment:
        l.sign(c)
    }
}

func (l *lexer) idInt(c uint8) {
    switch l.state {
    case state.IdInt1:
        if c == 'n' {
            l.state = state.IdInt2
            l.tokenValueAdd(c)
        } else {
            l.state = state.Id
            l.idInt(c)
        }
    case state.IdInt2:
        if c == 't' {
            l.state = state.IdInt3
            l.tokenValueAdd(c)
        } else {
            l.state = state.Id
            l.idInt(c)
        }
    case state.IdInt3:
        if c == ' ' {
            l.tokensAdd(token.Int)
            l.initial(c)
        } else {
            l.state = state.Id
            l.idInt(c)
        }
    case state.Id:
        if IsAlphabet(c) || IsNumber(c) || c == '_' {
            l.tokenValueAdd(c)
        } else {
            l.tokensAdd(token.Identifier)
            l.initial(c)
        }
    }
}

func (l *lexer) sign(c uint8) {
    switch l.state {
    case state.Plus:
        l.tokensAdd(token.Plus)
    case state.Minus:
        l.tokensAdd(token.Minus)
    case state.Star:
        l.tokensAdd(token.Star)
    case state.Slash:
        l.tokensAdd(token.Slash)
    case state.RightParen:
        l.tokensAdd(token.RightParen)
    case state.LeftParen:
        l.tokensAdd(token.LeftParen)
    case state.SemiColon:
        l.tokensAdd(token.SemiColon)
    case state.Assignment:
        l.tokensAdd(token.Assignment)
    }
    l.initial(c)
}

func (l *lexer) tokensAdd(t token.Type) {
    l.tokens = append(
        l.tokens,
        l.tokenBuilder(t, l.tokenValue, l.lineNumber),
    )
}

func (l *lexer) initial(c uint8) {
    l.tokenValue = ""
    l.state = state.Initial
    
    if c == 'i' {
        l.state = state.IdInt1
    } else if IsAlphabet(c) || c == '_' {
        l.state = state.Id
    } else if IsNumber(c) {
        l.state = state.IntLiteral
    } else if c == '+' {
        l.state = state.Plus
    } else if c == '-' {
        l.state = state.Minus
    } else if c == '*' {
        l.state = state.Star
    } else if c == '/' {
        l.state = state.Slash
    } else if c == '(' {
        l.state = state.LeftParen
    } else if c == ')' {
        l.state = state.RightParen
    } else if c == ';' {
        l.state = state.SemiColon
    } else if c == '=' {
        l.state = state.Assignment
    }
    
    if l.state != state.Initial {
        l.tokenValueAdd(c)
    }
}

func (l *lexer) tokenValueAdd(c uint8) {
    l.tokenValue += string(c)
}
